package cn.cool.netty.nio.selector;

import lombok.extern.java.Log;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

@Log
public class SelectorServer {
    public static void main(String[] args) throws IOException {
        // 1. 创建 Selector，管理多个 channel
        Selector selector = Selector.open();

        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false);

        // 2. 建立 selector 和 channel 的联系（注册 channel到selector上面）
        // SelectionKey 就是将来事件发生后，可以通过它得到事件类型和对应的 channel
        SelectionKey sscKey = ssc.register(selector, SelectionKey.OP_ACCEPT, null);
        // key 只关注 accept 事件

        ssc.bind(new InetSocketAddress(8080));

        while (true) {
            log.info("selector start...");
            // 3. selector的 select方法，没有事件发生时候就会阻塞，直到有事件发生才恢复
            // 事件要么处理，要么取消，不能置之不理
            selector.select();
            log.info("selector end...");
            // 4. 有事件发生，处理事件，selectionKeys内部包含了所有发生的事件
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                log.info("key: " + key);
                // 获取事件后，必须要从selectedKeys中移除，要不然会被重复处理这个 channel
                iterator.remove();
                // 5. 区分事件类型
                if (key.isAcceptable()) { // 如果是 accept
                    ServerSocketChannel channel = (ServerSocketChannel) key.channel();
                    SocketChannel socketChannel = channel.accept();
                    socketChannel.configureBlocking(false);
                    ByteBuffer buffer = ByteBuffer.allocate(16);
                    SelectionKey scKey = socketChannel.register(selector, 0, buffer);
                    scKey.interestOps(SelectionKey.OP_READ);
                    log.info("channel: " + socketChannel);
                } else if (key.isReadable()) { // 如果是read
                    try {
                        SocketChannel channel = (SocketChannel) key.channel();
                        // 获取该 channel的附件 attachment，即 ByteBuffer
                        ByteBuffer buffer = (ByteBuffer) key.attachment();
                        int read = channel.read(buffer);
                        //如果客户端正常断开，read返回-1
                        if (read == -1) {
                            key.cancel();
                            continue;
                        }

                        // 未压缩，说明长度超了
                        if (buffer.position() == buffer.capacity()) {
                            ByteBuffer newBuffer = ByteBuffer.allocate(buffer.capacity() * 2);
                            newBuffer.put(buffer);
                            key.attach(newBuffer);
                        }
                        split(buffer);
                    } catch (IOException e) {
                        e.printStackTrace();
                        key.cancel(); // 因为客户端断开了，所以需要将 key取消cancel（从selector的key集合中真正删除key）
                    }
                }
            }
        }
    }

    private static void split(ByteBuffer buffer) {
        // 开启读模式
        buffer.flip();
        for (int i = 0; i < buffer.limit(); i++) {
            if (buffer.get(i) == '\n') {
                int length = i - buffer.position() + 1;
                ByteBuffer allocation = ByteBuffer.allocate(length);
                for (int j = 0; j < length; j++) {
                    allocation.put(buffer.get());
                }

                allocation.flip();
                System.out.println(Charset.defaultCharset().decode(allocation));
            }
        }
        buffer.compact();
    }
}